#include "ground.h"
#include "particle.h"
#include "controlparameter.h"

ground::ground() {
    resize(640, 600);
    initParticle();

    //界面参数控制
    //ControlParameter* ControlParameterHandle = new ControlParameter(this);
    //ControlParameterHandle->show();

    timer = new QTimer(this);
    connect(timer, &QTimer::timeout,
            this,  &ground::onUpdate);
    timer->start(16);
}

void ground::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        qDebug() << "Ground clicked at" << event->pos();
        leftClick = true;
        MousePos = event->pos();
    }
    // 如果不想让父组件继续处理，直接 return；
    // 否则调用基类实现，保持默认行为
    QWidget::mousePressEvent(event);
}

void ground::mouseMoveEvent(QMouseEvent *event)
{
    if (leftClick) {
        qDebug() << "[Move]   拖动, 坐标:" << event->pos();
        MousePos = event->pos();
    }
    QWidget::mouseMoveEvent(event);
}

void ground::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        qDebug() << "[Release]左键松开, 坐标:" << event->pos();
        leftClick = false;
    }
    QWidget::mouseReleaseEvent(event);
}

float ground::IntrinsicPressurePower(float radio, float dis)
{
    if(dis >= radio)
        return 0;
    return mousePower;
}

QPointF ground::createIntrinsicPressure(particle* part, QPointF p, QPointF mouse, float Radio)
{
    QPointF rAB = p - mouse;
    float d = std::hypot(rAB.x(), rAB.y());
    // float density = Density(Radio, d);
    // float power = part->DensitytoPressure(density);
    float power = IntrinsicPressurePower(Radio, d);
    QPointF gvec = rAB/d;
    gvec *= power;
    part->applynewton3_add(gvec.x(),gvec.y(),1,1);
    return gvec;
}

void ground::initParticle()
{
    float BeginX = rect().width()/5;
    float BeginY = rect().height()/5;
    int nums = 100;
    float spacing = 2;
    float r = 2;
    int index = 0;
    for (int i = 0; i < nums; ++i) {
        for (int j = 0; j < nums; ++j) {
            QPointF pos(BeginX + j*spacing*r, BeginY + i*spacing*r);
            particle* particleHandle = new particle(pos,r,this);
            particleList.push_back(particleHandle);
            particleHandle->SetIndex(index);
            index++;
        }
    }
}

float ground::Density(float radio, float dis)
{
    if(dis >= radio)
        return 0;

    float volume = M_PI * pow(radio, 4.) / 6.;
    float value = (radio - dis) * (radio - dis) / volume;
    return value;
}

float ground::GetDistance(QPointF p1, QPointF p2)
{
    float v1 = p2.x()-p1.x();
    float v2 = p2.y()-p1.y();
    return sqrt(v1*v1+v2*v2);
}

float ground::GetDensity(QPointF p1, QPointF p2)
{
    float dis = GetDistance(p1, p2);
    float DensityValueTmp = Density(DensityRadio,dis);
    return DensityValueTmp;
}

float ground::GetDensityX(QPointF p1, QPointF p2, float Step)
{
    float dis = GetDistance(p1, QPointF(p2.x()+Step,p2.y()));
    float DensityValueTmp = Density(DensityRadio,dis);
    return DensityValueTmp;
}

float ground::GetDensityY(QPointF p1,    QPointF p2, float Step)
{
    float dis = GetDistance(p1, QPointF(p2.x(),p2.y()+Step));
    float DensityValueTmp = Density(DensityRadio,dis);
    return DensityValueTmp;
}

bool ground::collisionTarget(QPointF p1, QPointF p2, float r)
{
    QPointF rAB = p2 - p2;
    float d = std::hypot(rAB.x(), rAB.y());
    if(d <= r)
        return true;
    return false;
}

void ground::moveAlongConveyor(particle* part, float height, float power)
{
    float bottom = rect().height();
    if(part->GetPos().y() >= bottom-height)
    {
        float vx = power*sin(M_PI/2.);
        float vy = power*cos(M_PI/2.);
        if(part->GetPos().x()>rect().width()/2)
        {
            vx = -vx;
            vy = -vy;
        }
        part->applynewton3_add(vx,vy,1,1);
    }
}

qreal ground::mapRange(qreal value,
               qreal inMin,  qreal inMax,
               qreal outMin, qreal outMax,
               bool clamp)
{
    if (qFuzzyCompare(inMax, inMin))
        return outMin;                       // 防 / 0

    qreal t = (value - inMin) / (inMax - inMin);

    if (clamp)
        t = qBound(0.0, t, 1.0);

    return outMin + t * (outMax - outMin);
}

//网格法
void ground::traverseGrid(float h, float winW, float winH)
{
    const float cellSize = h; // 核半径
    int gx = std::max(1, int(std::ceil(winW / cellSize)));
    int gy = std::max(1, int(std::ceil(winH / cellSize)));
    int numCells = gx * gy;

    // 链表桶存储
    QVector<int> head(numCells, -1);
    QVector<int> next(particleList.size(), -1);

    auto clampi = [](int v, int lo, int hi){ return (v < lo) ? lo : (v > hi ? hi : v); };
    auto coordOf = [&](float x, float y) {
        int cx = int(std::floor(x / cellSize));
        int cy = int(std::floor(y / cellSize));
        cx = clampi(cx, 0, gx - 1);
        cy = clampi(cy, 0, gy - 1);
        return QPair<int,int>(cx, cy);
    };
    auto cellId = [&](int cx, int cy) { return cy * gx + cx; };

    // 建立桶
    for (int i = 0; i < particleList.size(); ++i) {
        auto [cx, cy] = coordOf(particleList[i]->GetPos().x(), particleList[i]->GetPos().y());
        int cid = cellId(cx, cy);
        next[i] = head[cid];
        head[cid] = i;
    }

    float Step = 0.1f;
    float dt = 0.016f;

    // 遍历每个粒子邻域
#ifdef CPU_POWER
    QtConcurrent::blockingMap(particleList, [=](particle* &pi) {
#endif
#ifndef CPU_POWER
        for (int i = 0; i < particleList.size(); ++i)
#endif
        {
#ifndef CPU_POWER
            auto [cx, cy] = coordOf(particleList[i]->GetPos().x(), particleList[i]->GetPos().y());
#else
            auto [cx, cy] = coordOf(pi->GetPos().x(), pi->GetPos().y());
#endif
            float DensityValueTmp = 0;
            float DensityValueTmpX = 0;
            float DensityValueTmpY = 0;
#ifndef CPU_POWER
            particle* pi = particleList[i];
#endif
            for (int oy = -1; oy <= 1; ++oy) {
                for (int ox = -1; ox <= 1; ++ox) {
                    int nx = clampi(cx + ox, 0, gx - 1);
                    int ny = clampi(cy + oy, 0, gy - 1);
                    int cid = cellId(nx, ny);

                    for (int j = head[cid]; j != -1; j = next[j]) {
#ifndef CPU_POWER
                        if (i == j) continue;
#else
                        if(pi->GetIndex()==particleList[j]->GetIndex())
                            continue;
#endif
                        float dx = particleList[j]->GetPos().x() - pi->GetPos().x();
                        float dy = particleList[j]->GetPos().y() - pi->GetPos().y();
                        float dist2 = dx*dx + dy*dy;
                        if (dist2 < h*h) {
                            // 在核半径内，进行 SPH 计算
                            QPointF rAB = particleList[j]->GetPos() - pi->GetPos();              // A → B
                            float   d   = std::hypot(rAB.x(), rAB.y());
                            if (d > DensityRadio)       // 防 / 0
                                continue;

                            if(d <= pi->GetR())
                            {
                                float fx =  -rAB.x();
                                float fy =  -rAB.y();
                                pi->applynewton3_add(fx,fy,mass,1);
                                particleList[j]->applynewton3_sub(fx,fy,mass,1);
                                continue;
                            }

                            float v0 = mass * GetDensity(particleList[j]->GetPos(),pi->GetPos());
                            float v1 = mass * GetDensityX(particleList[j]->GetPos(),pi->GetPos(),Step);
                            float v2 = mass * GetDensityY(particleList[j]->GetPos(),pi->GetPos(),Step);
                            DensityValueTmp += mass * v0;
                            DensityValueTmpX += mass * v1;
                            DensityValueTmpY += mass * v2;

                            float pA = pi->DensitytoPressure(pi->getdensity());
                            float pB = particleList[j]->DensitytoPressure(particleList[j]->getdensity());
                            float mag = 0.5f * (pA + pB);                 // 牛顿三定律
                            QPointF dirNorm(rAB.x()/d, rAB.y()/d);      //应该是 ->AB / |AB|
                            float fx =  -dirNorm.x() * mag;            // 沿 AB
                            float fy =  -dirNorm.y() * mag;
                            pi->applynewton3_add(fx,fy,mass,dt);
                            particleList[j]->applynewton3_sub(fx,fy,mass,dt);

                            QPointF vRel(pi->getvx() - particleList[j]->getvx(), pi->getvy() - particleList[j]->getvy());
                            float vrn = (vRel.x()*rAB.x() + vRel.y()*rAB.y()) / d;  // 法向速度
                            if (vrn > 0) {
                                float mu  = d * vrn / (d*d + 0.01f*DensityRadio*DensityRadio);
                                float visc= -visMu * mu;

                                float fxv = visc * dirNorm.x() / d;
                                float fyv = visc * dirNorm.y() / d;

                                pi->applynewton3_add(fxv,fyv,mass,dt);
                                particleList[j]->applynewton3_sub(fxv,fyv,mass,dt);
                            }

                            float w = 1.0f - d / DensityRadio;
                            QPointF xsph = w * xsphEta * vRel;

                            pi->xsph_sub(xsph.x(),xsph.y(),dt);
                            particleList[j]->xsph_add(xsph.x(),xsph.y(),dt);
                        }
                    }
                }
            }
            float fx = (DensityValueTmpX - DensityValueTmp)/Step;
            float fy = (DensityValueTmpY - DensityValueTmp)/Step;//压力已废除
            QPointF gvec(fx,fy);
            pi->SetGvec(gvec);
            pi->setdensity(DensityValueTmp);

            float ang = std::atan2(-gvec.x(), -gvec.y());
            float pressure = pi->DensitytoPressure(DensityValueTmp);

            pi->applygravity();
            //pi->applyPressure(ang,pressure);

            if(leftClick)
                createIntrinsicPressure(pi,pi->GetPos(),MousePos,50);

            //moveAlongConveyor(pi,5,0.5);
            float vx = pi->getvx();
            float vy = pi->getvy();
            float colorv = mapRange(sqrt(vx*vx + vy*vy),0,15,0,100);
            QColor color = speedToGradientColor(colorv);

            pi->SetColor(color);
            pi->moveStep();
        }
    });
}

QColor ground::speedToGradientColor(int value)
{
    value = qBound(0, value, 100);

    int i = 0;
    while (i < kStops.size() - 1 && value > kStops[i+1].pos)
        ++i;

    ColorStop a = kStops[i];
    ColorStop b = kStops[i+1];

    double t = double(value - a.pos) / (b.pos - a.pos);

    QColor ah = a.color.toHsl();
    QColor bh = b.color.toHsl();

    double h = ah.hslHueF()     + t * (bh.hslHueF()     - ah.hslHueF());
    double s = ah.hslSaturationF() + t * (bh.hslSaturationF() - ah.hslSaturationF());
    double l = ah.lightnessF()  + t * (bh.lightnessF()  - ah.lightnessF());

    QColor out;
    out.setHslF(h, s, l);
    return out;
}

void ground::onUpdate()
{
    traverseGrid(10,rect().width(),rect().height());
    update();
}

void ground::paintEvent(QPaintEvent * /*event*/)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
    painter.fillRect(rect(), Qt::white);
    painter.setPen(Qt::NoPen);
    for (int i = 0; i < particleList.size(); ++i) {
        QPointF center = particleList[i]->GetPos();
        QColor col = particleList[i]->GetColor();
        int R = particleList[i]->GetR();
        painter.setBrush(col);
        painter.drawEllipse(center, R, R);
    }
}
